﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Text;
using System.Threading;
using System.Text.RegularExpressions;
using System.IO;
using System.Web;
using System.Net;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using OpenMcdf;
using DirectShowLib;


namespace AutoEncodeDaemon
{
    // Stream to IStream wrapper for COM interop
    internal sealed class ComIStreamWrapper : IStream
    {
        private const int STG_E_INVALIDFUNCTION = unchecked((int)0x80030001);

        private readonly Stream baseStream;
        private long position = -1;

        internal ComIStreamWrapper(Stream stream)
        {
            baseStream = stream;
        }

        private void SetSizeToPosition()
        {
            if (position != -1)
            {
                if (position > baseStream.Length)
                    baseStream.SetLength(position);
                baseStream.Position = position;
                position = -1;
            }
        }

        public void Read(byte[] pv, int cb, IntPtr pcbRead)
        {
            int read = 0;

            if (cb != 0)
            {
                SetSizeToPosition();

                read = baseStream.Read(pv, 0, cb);
            }

            if (pcbRead != IntPtr.Zero)
                Marshal.WriteInt32(pcbRead, read);
        }

        public void Write(byte[] pv, int cb, IntPtr pcbWritten)
        {
            if (cb != 0)
            {
                SetSizeToPosition();

                baseStream.Write(pv, 0, cb);
            }

            if (pcbWritten != IntPtr.Zero)
                Marshal.WriteInt32(pcbWritten, cb);
        }

        public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
        {
            long newPosition;

            if (baseStream.CanWrite)
            {
                switch ((SeekOrigin)dwOrigin)
                {
                    case SeekOrigin.Begin:
                        newPosition = dlibMove;
                        break;
                    case SeekOrigin.Current:
                        if ((newPosition = position) == -1)
                            newPosition = baseStream.Position;
                        newPosition += dlibMove;
                        break;
                    case SeekOrigin.End:
                        newPosition = baseStream.Length + dlibMove;
                        break;
                    default:
                        throw new ExternalException(null, STG_E_INVALIDFUNCTION);
                }

                if (newPosition > baseStream.Length)
                    position = newPosition;
                else
                {
                    baseStream.Position = newPosition;
                    position = -1;
                }
            }
            else
            {
                try
                {
                    newPosition = baseStream.Seek(dlibMove, (SeekOrigin)dwOrigin);
                }
                catch (ArgumentException)
                {
                    throw new ExternalException(null, STG_E_INVALIDFUNCTION);
                }
                position = -1;
            }

            if (plibNewPosition != IntPtr.Zero)
                Marshal.WriteInt64(plibNewPosition, newPosition);
        }

        public void SetSize(long libNewSize)
        {
            baseStream.SetLength(libNewSize);
        }

        public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
        {
            byte[] buffer = new byte[4096];
            long written = 0;
            int read;

            if (cb != 0)
            {
                SetSizeToPosition();
                do
                {
                    int count = 4096;

                    if (written + 4096 > cb)
                        count = (int)(cb - written);

                    if ((read = baseStream.Read(buffer, 0, count)) == 0)
                        break;
                    pstm.Write(buffer, read, IntPtr.Zero);
                    written += read;
                } while (written < cb);
            }

            if (pcbRead != IntPtr.Zero)
                Marshal.WriteInt64(pcbRead, written);
            if (pcbWritten != IntPtr.Zero)
                Marshal.WriteInt64(pcbWritten, written);
        }

        public void Commit(int grfCommitFlags)
        {
            baseStream.Flush();
        }

        public void Revert()
        {
            throw new ExternalException(null, STG_E_INVALIDFUNCTION);
        }

        public void LockRegion(long libOffset, long cb, int dwLockType)
        {
            throw new ExternalException(null, STG_E_INVALIDFUNCTION);
        }

        public void UnlockRegion(long libOffset, long cb, int dwLockType)
        {
            throw new ExternalException(null, STG_E_INVALIDFUNCTION);
        }

        public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
        {
            pstatstg = new System.Runtime.InteropServices.ComTypes.STATSTG();
            pstatstg.cbSize = baseStream.Length;
        }

        public void Clone(out IStream ppstm)
        {
            ppstm = null;
            throw new ExternalException(null, STG_E_INVALIDFUNCTION);
        }
    }
    public class CookieAwareWebClient : WebClient
    {
        public CookieContainer cookie = new CookieContainer();
        public string refer = null;
        protected override WebRequest GetWebRequest(Uri address)
        {
            WebRequest request = base.GetWebRequest(address);
            if (request is HttpWebRequest)
            {
                (request as HttpWebRequest).CookieContainer = cookie;
                (request as HttpWebRequest).Referer = refer;
                (request as HttpWebRequest).KeepAlive = false;
            }
            return request;
        }
    }
    public class JobScheduler
    {
        public class audioInfo
        {
            public string streamID = "";
            public string streamDelay = "";
            public string trackID = "";
            public string audioPath = "";
        }
        public class subtileInfo
        {
            public string PID = "";
            public string subtileLanguage = "";
            public string subtileDelay = "";
            public string idxPath = "";
            public string subPath = "";
        }
        private Semaphore sem;
        private AutoEncodeDBContext db;
        private DateTime LastReleaseTime;
        private int MinReleaseInterval;
        private int[] CurrentEncodeJob;
        private int MaxReleaseJob;
        private int CurrentReleaseJob;
        public JobScheduler(Semaphore s)
        {
            sem = s;
            db = new AutoEncodeDBContext();
            LastReleaseTime = DateTime.MinValue;
            CurrentEncodeJob = new int[Properties.Settings.Default.x264.Count];
            CurrentReleaseJob = 0;
            MaxReleaseJob = 1;
            MinReleaseInterval = 1800;
            // change all Processing job to Wating
            var encoderesult = from encode in db.EncodeJobs
                               where encode.JobStatusIndex == db.JobStatusIndex.First(x => x.JobStatusName == "Processing")
                               select encode;
            foreach (var encode in encoderesult)
            {
                db.ChangeEncodeJobStatus(encode, "Wating");
            }
            var releaseresult = from release in db.ReleaseJobs
                                where release.JobStatusIndex == db.JobStatusIndex.First(x => x.JobStatusName == "Processing")
                                select release;
            foreach (var release in releaseresult)
            {
                db.ChangeReleaseJobStatus(release, "Wating");
            }
        }
        public int CheckEncodeOk()
        {
            for (int i = 0; i < CurrentEncodeJob.Length; i++)
            {
                if (CurrentEncodeJob[i] < Convert.ToInt32(Properties.Settings.Default.x264[i].Split(new char[] { '|' })[5])) return i;
            }
            return -1;
        }
        public bool CheckReleaseOk()
        {
            if (CurrentReleaseJob >= MaxReleaseJob) return false;
            if (DateTime.Now.Subtract(LastReleaseTime).TotalSeconds <= MinReleaseInterval) return false;
            if (DateTime.Now.Hour >= 23 || DateTime.Now.Hour == 0) return true;
            if (DateTime.Now.Hour >= 6 && DateTime.Now.Hour <= 14) return true;
            return false;
        }
        public List<string> GetFiles(string FileOrDirPath)
        {
            List<string> result = new List<string>();
            if (Directory.Exists(FileOrDirPath) == true)
            {
                foreach (var file in Directory.GetFiles(FileOrDirPath))
                {
                    result.Add(file);
                }
            }
            else result.Add(FileOrDirPath);
            return result;
        }
        public bool DoTemplateMatch()
        {
            // check new torrent job if its match a filter
            var tempresults = from torrent in db.TorrentJobs
                              from filter in db.TemplateFilter
                              where torrent.JobStatusIndex.JobStatusName == "New"
                              select new { torrent, filter };
            var results = from table in tempresults.ToList()
                          where Regex.IsMatch(table.torrent.TorrentTitle, table.filter.FilterMatch, RegexOptions.IgnoreCase) == true
                          select table;
            // if so, create corresponding job
            foreach (var entry in results)
            {
                EncodeJobs EncodeJob;
                ReleaseJobs ReleaseJob = null;
                AutoEncodeLog.Log2File(String.Format("TorrentJob with name {0} has successfully matched one filter named \"{1}\"", entry.torrent.TorrentTitle, entry.filter.FilterMatch));
                db.ChangeTorrentJobStatus(entry.torrent, "Done");
                if (entry.filter.Template.ReleaseEachFile == false)
                {
                    string newname = entry.torrent.TorrentPath;
                    newname = Regex.Replace(newname, "[ ]{1,}", @".", RegexOptions.IgnoreCase);
                    if (entry.filter.Template.ResizeHight.GetValueOrDefault() != 0) newname = Regex.Replace(newname, "1080i", entry.filter.Template.ResizeHight.GetValueOrDefault().ToString() + "p", RegexOptions.IgnoreCase);
                    newname = Regex.Replace(newname, "MPEG2", "x264", RegexOptions.IgnoreCase);
                    newname = Regex.Replace(newname, "MPEG.2", "x264", RegexOptions.IgnoreCase);
                    newname = Regex.Replace(newname, "H264", "x264", RegexOptions.IgnoreCase);
                    newname = Regex.Replace(newname, "H.264", "x264", RegexOptions.IgnoreCase);
                    newname = Regex.Replace(newname, "-.*?@CHDTV", "-CHDTV", RegexOptions.IgnoreCase);
                    ReleaseJob = db.AddReleaseJob(entry.filter.Template.CatalogIndex, ReleaseEnvironment.GetReleaseName(newname, Directory.Exists(entry.torrent.TorrentPath)), entry.filter.Template.SubTitle, entry.filter.Template.Url, entry.filter.Template.AudioLanguage,
                        entry.filter.Template.Sub, entry.filter.Template.Author, entry.filter.Template.Description, entry.filter.Template.Language, entry.filter.Template.Poster, entry.filter.Template.CastImg,
                        db.JobStatusIndex.First(x => x.JobStatusName == "Wating"), entry.filter.Template.NfoTemplate, entry.filter.Template.DescriptionTemplate);
                }
                foreach (var file in GetFiles(entry.torrent.TorrentPath))
                {
                    string outputFileName;
                    outputFileName = Path.GetFileNameWithoutExtension(file);
                    outputFileName += ".mkv";
                    // replace outputfilename with . and 720p x264 etc
                    outputFileName = Regex.Replace(outputFileName, "[ ]{1,}", @".", RegexOptions.IgnoreCase);
                    if (entry.filter.Template.ResizeHight.GetValueOrDefault() != 0) outputFileName = Regex.Replace(outputFileName, "1080i", entry.filter.Template.ResizeHight.GetValueOrDefault().ToString() + "p", RegexOptions.IgnoreCase);
                    outputFileName = Regex.Replace(outputFileName, "MPEG2", "x264", RegexOptions.IgnoreCase);
                    outputFileName = Regex.Replace(outputFileName, "MPEG.2", "x264", RegexOptions.IgnoreCase);
                    outputFileName = Regex.Replace(outputFileName, "H264", "x264", RegexOptions.IgnoreCase);
                    outputFileName = Regex.Replace(outputFileName, "H.264", "x264", RegexOptions.IgnoreCase);
                    outputFileName = Regex.Replace(outputFileName, "-.*?@CHDTV", "-CHDTV", RegexOptions.IgnoreCase);
                    outputFileName = Path.Combine(Path.GetDirectoryName(file), outputFileName);

                    EncodeJob = db.AddEncodeJob(entry.torrent, file, outputFileName, entry.filter.Template.TargetFileSize, entry.filter.Template.TargetAudioBitrate.GetValueOrDefault(),
                        entry.filter.Template.ResizeHight.GetValueOrDefault(), entry.filter.Template.ResizeWidth.GetValueOrDefault(),
                        entry.filter.Template.CropLeft.GetValueOrDefault(), entry.filter.Template.CropRight.GetValueOrDefault(), entry.filter.Template.CropTop.GetValueOrDefault(), entry.filter.Template.CropBottom.GetValueOrDefault(),
                        db.JobStatusIndex.First(x => x.JobStatusName == "New"));
                    if (entry.filter.Template.ReleaseEachFile == true)
                    {
                        ReleaseJob = db.AddReleaseJob(entry.filter.Template.CatalogIndex, ReleaseEnvironment.GetReleaseName(outputFileName, false), entry.filter.Template.SubTitle, entry.filter.Template.Url, entry.filter.Template.AudioLanguage,
                            entry.filter.Template.Sub, entry.filter.Template.Author, entry.filter.Template.Description, entry.filter.Template.Language, entry.filter.Template.Poster, entry.filter.Template.CastImg,
                            db.JobStatusIndex.First(x => x.JobStatusName == "Wating"), entry.filter.Template.NfoTemplate, entry.filter.Template.DescriptionTemplate);
                    }
                    EncodeJob.ReleaseJobs = ReleaseJob;
                    db.SubmitChanges();
                    db.ChangeEncodeJobStatus(EncodeJob, "Wating");
                }
            }
            return true;
        }
        private List<audioInfo> DemuxAudioSubTitle(string tsfile, out List<subtileInfo> subs)
        {
            List<audioInfo> audioTracks = new List<audioInfo>();
            subs = new List<subtileInfo>();
            bool hasSubtitle = false;
            // grab all tracks
            string eac3toOutput = ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.eac3to, "\"" + tsfile + "\"").Replace("\b", "");
            string[] result = Regex.Split(eac3toOutput, "\r\n");
            for (int i = 0; i < result.Length; i++)
            {
                Match match;
                match = Regex.Match(result[i], @"^(\d+):\s*(.+?)(,.*)");
                if (match.Success == true)
                {
                    if (match.Groups[2].Value.Contains("MPEG2") == true) continue;
                    if (match.Groups[2].Value.Contains("h264") == true) continue;
                    if (match.Groups[2].Value.Contains("AVC") == true) continue;
                    if (match.Groups[2].Value.Contains("Subtitle") == true) { hasSubtitle = true; continue; }
                    if (match.Groups[3].Value.Contains("1080") == true) continue;
                    if (match.Groups[3].Value.Contains("16:9") == true) continue;
                    string logfile;
                    audioInfo temptrack = new audioInfo();
                    temptrack.trackID = match.Groups[1].Value;
                    temptrack.streamID = match.Groups[2].Value;
                    Match delaymatch = Regex.Match(match.Groups[3].Value, @", (.\d*)ms");
                    if (delaymatch.Success == true) temptrack.streamDelay = delaymatch.Groups[1].Value;
                    else temptrack.streamDelay = "";
                    temptrack.audioPath = Path.Combine(Properties.Settings.Default.TempPath, Path.GetFileNameWithoutExtension(tsfile) + ".track_" + temptrack.trackID + "." + temptrack.streamID);
                    logfile = Path.Combine(Properties.Settings.Default.TempPath, Path.GetFileNameWithoutExtension(tsfile) + ".track_" + temptrack.trackID + " - Log.txt");
                    string demuxresult = ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.eac3to, "\"" + tsfile + "\" " + temptrack.trackID + ": \"" + temptrack.audioPath + "\"");
                    File.Delete(logfile);
                    if (File.Exists(temptrack.audioPath))
                    {
                        if (demuxresult.Contains("Done.") == true)
                        {
                            audioTracks.Add(temptrack);
                        }
                        else
                        {
                            AutoEncodeLog.Log2File(String.Format("eac3to has demuxed audio track {0} from file \"{1}\", but its console output didnt' include \"Done.\". eac3to output: {2}", temptrack.trackID, tsfile, demuxresult));
                            File.Delete(temptrack.audioPath);
                        }
                    }
                    else
                    {
                        AutoEncodeLog.Log2File(String.Format("eac3to failed to demux audio track {0} from file \"{1}\". eac3to output: {2}", temptrack.trackID, tsfile, demuxresult));
                    }
                }
            }
            // demux subtitle
            if (hasSubtitle == true)
            {
                string subInfo = ReleaseEnvironment.exeAndGetError(Properties.Settings.Default.ffmpegPath, "-i \"" + tsfile + "\"");
                string videopid = "";
                long videopts=0;
                string[] subResult = Regex.Split(subInfo, "\r\n");
                for (int i = 0; i < subResult.Length; i++)
                {
                    Match match;
                    match = Regex.Match(subResult[i], @"^\s*Stream #[\d.]+\[(0x[A-Fa-f0-9]+)\]: Video");
                    if (match.Success == true)
                    {
                        videopid = match.Groups[1].Value;
                    }
                    match = Regex.Match(subResult[i], @"^\s*Stream #[\d.]+\[(0x[A-Fa-f0-9]+)\]\((.*?)\): Subtitle");
                    if (match.Success == true)
                    {
                        subtileInfo temptrack = new subtileInfo();
                        temptrack.PID = match.Groups[1].Value;
                        temptrack.subtileLanguage = match.Groups[2].Value;
                        subs.Add(temptrack);
                    }
                }
                if (subs.Count != 0 && String.IsNullOrEmpty(videopid) == false)
                {
                    string PIDs="";
                    string logfile;
                    string pid="";
                    foreach (var track in subs)
                    {
                        PIDs += track.PID+",";
                    }
                    PIDs = PIDs.TrimEnd(new char[] {','});
                    logfile = Path.Combine(Properties.Settings.Default.TempPath, Path.GetFileNameWithoutExtension(tsfile) + "_log.txt");
                    ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.JavaPath, "-jar \"" + Properties.Settings.Default.ProjectX + "\" -ini \"" + Properties.Settings.Default.ProjectXConfig + "\" -id " + PIDs + " -demux \"" + tsfile + "\" -out \"" + Properties.Settings.Default.TempPath + "\"");
                    foreach (string line in File.ReadAllLines(logfile))
                    {
                        Match match;
                        match = Regex.Match(line, @"^\+\+> .*?: PID (0x[A-Fa-f0-9]+) .*:");
                        if (match.Success == true)
                        {
                            pid = "";
                            var tracks = from sub in subs
                                         where Convert.ToInt32(sub.PID, 16) == Convert.ToInt32(match.Groups[1].Value, 16)
                                         select sub;
                            if (tracks.Count() != 0)
                            {
                                pid = match.Groups[1].Value;
                            }
                        }
                        if (String.IsNullOrEmpty(pid) == true) continue;
                        match = Regex.Match(line, @"^---> new File: (.*\.sup)");
                        if (match.Success == true)
                        {
                            File.Delete(match.Groups[1].Value);
                            File.Delete(match.Groups[1].Value + ".IFO");
                        }
                        match = Regex.Match(line, @"^-> .* PTS: first packet ([\d.:]+),");
                        if (match.Success == true)
                        {
                            var tracks = from sub in subs
                                         where Convert.ToInt32(sub.PID, 16) == Convert.ToInt32(pid, 16)
                                         select sub;
                            tracks.First().subtileDelay = match.Groups[1].Value;
                        }
                        match = Regex.Match(line, @"^-> create VobSub Files \(idx \+ sub\) : (.*\.sup)\.sub");
                        if (match.Success == true)
                        {
                            var tracks = from sub in subs
                                         where Convert.ToInt32(sub.PID, 16) == Convert.ToInt32(pid, 16)
                                         select sub;
                            tracks.First().idxPath = match.Groups[1].Value + ".idx";
                            tracks.First().subPath = match.Groups[1].Value + ".sub";
                        }
                    }
                    File.Delete(logfile);
                    subs.RemoveAll(x => String.IsNullOrEmpty(x.idxPath) == true || String.IsNullOrEmpty(x.subPath) == true || String.IsNullOrEmpty(x.subtileDelay) == true);
                    // fix sub delay
                    pid = "";
                    foreach (string line in Regex.Split(ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.tsreport, "-b \"" + tsfile + "\""), "\r\n"))
                    {
                        Match match;
                        match = Regex.Match(line, @"^Stream \d+: PID ([A-Fa-f0-9]+) \(\d+\)");
                        if (match.Success == true)
                        {
                            pid = match.Groups[1].Value;
                        }
                        if (String.IsNullOrEmpty(pid) == true) continue;
                        match = Regex.Match(line, @"^\s*First PTS ([\d]+)t,");
                        if (match.Success == true)
                        {
                            long pts = Convert.ToInt64(match.Groups[1].Value);
                            if (Convert.ToInt32(pid, 16) == Convert.ToInt32(videopid, 16))
                            {
                                videopts = pts;
                            }
                            else
                            {
                                var tracks = from sub in subs
                                             where Convert.ToInt32(sub.PID, 16) == Convert.ToInt32(pid, 16)
                                             select sub;
                                if (tracks.Count() != 0)
                                {
                                    tracks.First().subtileDelay = ((int)((pts-videopts)/90.0-TimeSpan.Parse(tracks.First().subtileDelay).TotalMilliseconds)).ToString();
                                }
                            }
                        }
                    }
                }
                subs.RemoveAll(x => String.IsNullOrEmpty(x.idxPath) == true || String.IsNullOrEmpty(x.subPath) == true || String.IsNullOrEmpty(x.subtileDelay) == true || x.subtileDelay.Contains(':'));
            }
            if (audioTracks.Count == 0)
            {
                AutoEncodeLog.Log2File(String.Format("eac3to can't find any audio track in file \"{0}\". eac3to output: {1}", tsfile, eac3toOutput));
            }
            return audioTracks;
        }
        private string DGIndexVideo(string tsfile)
        {
            string indexfile = Path.Combine(Properties.Settings.Default.TempPath, Path.GetFileNameWithoutExtension(tsfile));
            // check if need dgindex
            string format = ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.MediaInfoPath, "--Inform=Video;%Format% \"" + tsfile + "\"");
            if (format.Contains("MPEG") == true)
            {
                ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.DGIndex, "-i \"" + tsfile + "\" -o \"" + indexfile + "\" -om 0 -hide -exit");
                indexfile += ".d2v";
                File.Delete(Path.Combine(Path.GetDirectoryName(tsfile), Path.GetFileNameWithoutExtension(tsfile) + ".log"));
                if (File.Exists(indexfile) == true) return indexfile;
                else
                {
                    AutoEncodeLog.Log2File(String.Format("DGIndex failed to index  file \"{0}\"", tsfile));
                    return "";
                }
            }
            else
            {
                if (Properties.Settings.Default.DirectShowAutoRendering == true) return "DirectShowSource";
                indexfile += ".grf";
                FilterGraph fg = new FilterGraph();
                IGraphBuilder gb = (IGraphBuilder)fg;
                IPersistStream ps = (IPersistStream)fg;
                CompoundFile grf = new CompoundFile();

                if (Properties.Settings.Default.UseHardcodingFilter == true)
                {
                    IBaseFilter fsourcefile;
                    IBaseFilter fhaalisplitter;
                    //IBaseFilter fcoreavc;
                    IBaseFilter fcyberlink;
                    IPin poutsourcefile;
                    IPin pinhaali, pvouthaali;
                    //IPin pincoreavc;
                    IPin pincyberlink;
                    //gb.AddSourceFilter(tsfile, "File Source (Async.)", out fsourcefile);
                    fsourcefile=(IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("E436EBB5-524F-11CE-9F53-0020AF0BA770")));
                    ((IFileSourceFilter)fsourcefile).Load(tsfile, null);
                    gb.AddFilter(fsourcefile, "File Source (Async.)");
                    fhaalisplitter = (IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("564FD788-86C9-4444-971E-CC4A243DA150")));
                    gb.AddFilter(fhaalisplitter, "Haali Media Splitter (AR)");
                    //fcoreavc = (IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("09571A4B-F1FE-4C60-9760-DE6D310C7C31")));
                    //gb.AddFilter(fcoreavc, "CoreAVC Video Decoder");
                    fcyberlink = (IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("D00E73D7-06F5-44F9-8BE4-B7DB191E9E7F")));
                    gb.AddFilter(fcyberlink, "CyberLink Video Decoder (PDVD12)");
                    fsourcefile.FindPin("Output", out poutsourcefile);
                    fhaalisplitter.FindPin("Input", out pinhaali);
                    gb.Connect(poutsourcefile, pinhaali);
                    fhaalisplitter.FindPin("Video", out pvouthaali);
                    fcyberlink.FindPin("Video In", out pincyberlink);
                    gb.Connect(pvouthaali, pincyberlink);
                }
                else
                {
                    IBaseFilter VideoRenderer;
                    gb.RenderFile(tsfile, "");
                    gb.FindFilterByName("Video Renderer", out VideoRenderer);
                    gb.RemoveFilter(VideoRenderer);
                }
                CFStream grfstream = grf.RootStorage.AddStream("ActiveMovieGraph");
                MemoryStream ms = new MemoryStream();
                ComIStreamWrapper mswrapper = new ComIStreamWrapper(ms);
                ps.Save(mswrapper, true);
                grfstream.CopyFrom(ms);
                grf.Save(indexfile);
                grf.Close();
                return indexfile;
            }
        }
        private string CreateAVS(string tsfile, string d2vfile, int CropLeft, int CropRight, int CropTop, int CropBottom, int ResizeHight, int ResizeWidth)
        {
            string avs = Path.Combine(Properties.Settings.Default.TempPath, Path.GetFileNameWithoutExtension(tsfile) + ".avs");
            string interlaced, scanorder = "";
            if (ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.MediaInfoPath, "--Inform=Video;%ScanType% \"" + tsfile + "\"").Contains("Interlaced") == true)
            {
                interlaced = "true";
                if (ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.MediaInfoPath, "--Inform=Video;%ScanOrder% \"" + tsfile + "\"").Contains("TFF") == true)
                    scanorder = "1";
                else
                    scanorder = "0";
            }
            else if (ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.MediaInfoPath, "--Inform=Video;%ScanType% \"" + tsfile + "\"").Contains("MBAFF") == true)
            {
                interlaced = "true";
                scanorder = "-1";
            }
            else
                interlaced = "false";
            using (StreamWriter sw = new StreamWriter(avs,false, Encoding.Default))
            {
                sw.WriteLine("LoadPlugin(\"" + Properties.Settings.Default.DGDecode + "\")");
                if (d2vfile == "DirectShowSource" || d2vfile.EndsWith(".grf") == true)
                {
                    decimal fps = Convert.ToDecimal(ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.MediaInfoPath, "--Inform=Video;%FrameRate% \"" + tsfile + "\""));
                    if (d2vfile == "DirectShowSource") sw.WriteLine("DirectShowSource(\"" + tsfile + "\", audio=false, convertfps=true).AssumeFPS(" + fps + ")");
                    else sw.WriteLine("DirectShowSource(\"" + d2vfile + "\", audio=false, convertfps=true).AssumeFPS(" + fps + ")");
                    sw.WriteLine("ConvertToYV12(interlaced=" + interlaced + ")");
                }
                else
                {
                    sw.WriteLine("DGDecode_mpeg2source(\"" + d2vfile + "\", info=3)");
                    sw.WriteLine("LoadPlugin(\"" + Properties.Settings.Default.ColorMatrix + "\")");
                    sw.WriteLine("ColorMatrix(hints=true, interlaced=" + interlaced + ", threads=0)");
                }
                if (interlaced == "true")
                {
                    sw.WriteLine("Load_Stdcall_Plugin(\"" + Properties.Settings.Default.yadif + "\")");
                    sw.WriteLine("Yadif(order=" + scanorder + ")");
                }
                sw.WriteLine("crop( " + CropLeft + ", " + CropTop + ", -" + CropRight + ", -" + CropBottom + ")");
                sw.WriteLine("");
                if (ResizeHight != 0 && ResizeWidth != 0)
                    sw.WriteLine("LanczosResize(" + ResizeWidth + "," + ResizeHight + ") # Lanczos (Sharp)");
                sw.WriteLine("#denoise");
            }
            return avs;
        }
        private int CalcBitrate(string tsfile, long targetsize, int resizehight, int resizewidth, List<audioInfo> audioTracks)
        {
            AudioBitrateCalculationStream[] audios = new AudioBitrateCalculationStream[audioTracks.Count];
            CalcData data;
            decimal fps;
            long frames;
            if (targetsize < Properties.Settings.Default.TargetSizeBitrateThreshold) return (int)targetsize;
            if (audioTracks.Count == 0) return 0;
            for (int i = 0; i < audioTracks.Count; i++)
            {
                audios[i] = new AudioBitrateCalculationStream(audioTracks[i].audioPath);
            }
            fps = Convert.ToDecimal(ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.MediaInfoPath, "--Inform=Video;%FrameRate% \"" + tsfile + "\""));
            frames = Convert.ToInt64(ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.MediaInfoPath, "--Inform=Video;%FrameCount% \"" + tsfile + "\""));
            if (resizehight == 0) resizehight = Convert.ToInt32(ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.MediaInfoPath, "--Inform=Video;%Height% \"" + tsfile + "\""));
            if (resizewidth == 0) resizewidth = Convert.ToInt32(ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.MediaInfoPath, "--Inform=Video;%Width% \"" + tsfile + "\""));
            data = new CalcData(frames, fps, ContainerType.MKV, VideoCodec.AVC, true, audios);
            data.FrameSize = new Size(resizewidth, resizehight);
            data.TotalSize = new FileSize(targetsize);
            data.CalcByTotalSize();
            if (data.VideoBitrate > Properties.Settings.Default.MaxBitrate || data.VideoBitrate < Properties.Settings.Default.MinBitrate) return Properties.Settings.Default.FallbackBitrate;
            else return (int)data.VideoBitrate;
        }
        public string x264Encode(string tsfile, string avsfile, int bitrate, int x264index)
        {
            bool remoteencode;
            string stats, mp4file, remotestats, remotemp4file,remoteuncstats,remoteuncmp4file,remotetsfile;
            stats = Path.Combine(Properties.Settings.Default.TempPath, Path.GetFileNameWithoutExtension(tsfile) + ".stats");
            mp4file = Path.Combine(Properties.Settings.Default.TempPath, Path.GetFileNameWithoutExtension(tsfile) + ".mp4");
            if (String.IsNullOrEmpty(Properties.Settings.Default.x264[x264index].Split(new char[] { '|' })[6]) == false)
            {
                remoteencode = true;
                remotestats = Path.Combine(Properties.Settings.Default.x264[x264index].Split(new char[] { '|' })[6], Path.GetFileNameWithoutExtension(tsfile) + ".stats");
                remotemp4file = Path.Combine(Properties.Settings.Default.x264[x264index].Split(new char[] { '|' })[6], Path.GetFileNameWithoutExtension(tsfile) + ".mp4");
                remoteuncstats = Path.Combine(Properties.Settings.Default.x264[x264index].Split(new char[] { '|' })[0].Replace("@",@"\\"), remotestats.Replace(':', '$'));
                remoteuncmp4file = Path.Combine(Properties.Settings.Default.x264[x264index].Split(new char[] { '|' })[0].Replace("@", @"\\"), remotemp4file.Replace(':', '$'));
                remotetsfile = Path.Combine(Properties.Settings.Default.x264[x264index].Split(new char[] { '|' })[0].Replace("@", @"\\"), tsfile.Replace(':', '$'));
                Directory.CreateDirectory(Path.GetDirectoryName(remotetsfile));
                File.Copy(tsfile, remotetsfile,true);
            }
            else
            {
                remoteencode = false;
                remotestats = stats;
                remotemp4file = mp4file;
                remoteuncstats="";
                remoteuncmp4file="";
                remotetsfile = "";
            }
            string x264result1 = "", x264result2 = "";
            x264result1 = ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.x264[x264index],
                Properties.Settings.Default.x264pass1.Replace("$BITRATE", bitrate.ToString()).Replace("$STATS", remotestats).Replace("$AVSFILE", avsfile).Replace("$MP4FILE", remotemp4file));
            //"--pass 1 --bitrate " + bitrate + " --no-mbtree --stats \"" + stats + "\" --level 4.1 --ref 1 --bframes 3 --b-pyramid normal  --direct auto --b-adapt 2 --deblock -3:-3 --subme 1 --rc-lookahead 50 --me dia --trellis 0 --vbv-bufsize 30000 --vbv-maxrate 30000 --merange 24 --no-8x8dct --output NUL \"" + avsfile + "\" --aq-strength 1.0 --qcomp 0.7");
            if (Properties.Settings.Default.x2642pass == true)
            {
                x264result2 = ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.x264[x264index],
                    Properties.Settings.Default.x264pass2.Replace("$BITRATE", bitrate.ToString()).Replace("$STATS", remotestats).Replace("$AVSFILE", avsfile).Replace("$MP4FILE", remotemp4file));
            }
            if (remoteencode == true)
            {
                try
                {
                    File.Delete(remotetsfile);
                    File.Move(remoteuncstats, stats);
                    File.Move(remoteuncmp4file, mp4file);
                }
                catch (FileNotFoundException nf)
                {
                }
            }
            // old
            //"--level 4.1 --pass 2 --bitrate " + bitrate + " --stats  \"" + stats + "\" --deblock -3:-3 --ref 5 --chroma-qp-offset -1 --vbv-bufsize 30000 --vbv-maxrate 30000 --no-mbtree --merange 24 --me umh --direct auto --no-fast-pskip --output \"" + mp4file + "\" \"" + avsfile + "\"");
            // new
            //"--pass 2 --bitrate " + bitrate + " --no-mbtree --stats \"" + stats + "\" --level 4.1 --ref 5 --mixed-refs --no-fast-pskip --bframes 3 --b-pyramid normal --weightb --rc-lookahead 50 --direct auto --deblock -3:-3 --merange 24 --vbv-bufsize 30000 --vbv-maxrate 30000 --subme 8 --trellis 1 --partitions p8x8,b8x8,i4x4,i8x8 --8x8dct --chroma-qp-offset 1 --me umh --threads auto --output \"" + mp4file + "\" \"" + avsfile + "\" --aq-strength 1.0 --qcomp 0.7");
            File.Delete(stats);
            if (File.Exists(mp4file) == true) return mp4file;
            else
            {
                AutoEncodeLog.Log2File(String.Format("x264 failed to encode file \"{0}\". x264 first pass output: {1}. x264 second pass output: {2}", tsfile, x264result1, x264result2));
                return "";
            }
        }
        public string mkvMerge(string mp4file, List<audioInfo> audioTracks, List<subtileInfo> subtileTracks)
        {
            string mkvfile = Path.Combine(Properties.Settings.Default.TempPath, Path.GetFileNameWithoutExtension(mp4file) + ".mkv");
            string cmd, trackorder;
            string mkvMergeresult;
            int trackNo = 1;
            cmd = "-o \"" + mkvfile + "\" --forced-track 1:no -d 1 -A -S -T --no-global-tags --no-chapters \"" + mp4file + "\"";
            trackorder = "0:1";
            for (int i = 0; i < audioTracks.Count; i++)
            {
                //todo
                //debug
                //if (String.IsNullOrEmpty(audioTracks[i].streamDelay) == false) cmd += " --sync 0:" + audioTracks[i].streamDelay;
                cmd += " --forced-track 0:no -a 0 -D -S -T --no-global-tags --no-chapters \"" + audioTracks[i].audioPath + "\"";
                trackorder += "," + (trackNo++) + ":0";
            }
            for (int i = 0; i < subtileTracks.Count; i++)
            {
                if (String.IsNullOrEmpty(subtileTracks[i].subtileDelay) == false) cmd += " --sync 0:" + subtileTracks[i].subtileDelay;
                cmd += " --forced-track 0:no -A -D -s 0 -T --no-global-tags --no-chapters \"" + subtileTracks[i].idxPath + "\"";
                trackorder += "," + (trackNo++) + ":0";
            }
            cmd += " --track-order \"" + trackorder + "\"";
            mkvMergeresult = ReleaseEnvironment.exeAndGetOutput(Properties.Settings.Default.mkvMerge, cmd);
            if (File.Exists(mkvfile) == true) return mkvfile;
            else
            {
                string logMessage = String.Format("mkvMerge failed to merge file \"{0}\" with ", mp4file);
                foreach (var track in audioTracks)
                {
                    logMessage += "\"" + track.audioPath + "\" ";
                }
                logMessage += ". mkvMerge output: " + mkvMergeresult;
                AutoEncodeLog.Log2File(logMessage);
                return "";
            }
        }
        public void EncodeWorker(EncodeJobs Job, int x264index)
        {
            string tsfile = Job.InputFilePath;
            List<audioInfo> audioTracks;
            List<subtileInfo> subtileTracks;
            int bitrate;
            string d2vfile, avsfile, mp4file, mkvfile;
            try
            {
                // demux audio
                audioTracks = DemuxAudioSubTitle(tsfile, out subtileTracks);
                if (audioTracks.Count == 0)
                {
                    db.ChangeEncodeJobStatus(Job, "Failed");
                }
                else
                {
                    // dgindex video
                    d2vfile = DGIndexVideo(tsfile);
                    if (String.IsNullOrEmpty(d2vfile) == true)
                    {
                        db.ChangeEncodeJobStatus(Job, "Failed");
                    }
                    else
                    {
                        // create avs
                        avsfile = CreateAVS(tsfile, d2vfile, Job.CropLeft.GetValueOrDefault(), Job.CropRight.GetValueOrDefault(), Job.CropTop.GetValueOrDefault(), Job.CropBottom.GetValueOrDefault(), Job.ResizeHight.GetValueOrDefault(), Job.ResizeWidth.GetValueOrDefault());
                        if (String.IsNullOrEmpty(avsfile) == true)
                        {
                            db.ChangeEncodeJobStatus(Job, "Failed");
                        }
                        else
                        {
                            // calculate bitrate
                            bitrate = CalcBitrate(tsfile, Job.TargetFileSize, Job.ResizeHight.GetValueOrDefault(720), Job.ResizeWidth.GetValueOrDefault(1280), audioTracks);
                            if (bitrate == 0)
                            {
                                db.ChangeEncodeJobStatus(Job, "Failed");
                            }
                            else
                            {
                                mp4file = x264Encode(tsfile, avsfile, bitrate, x264index);
                                if (String.IsNullOrEmpty(mp4file) == true)
                                {
                                    db.ChangeEncodeJobStatus(Job, "Failed");
                                }
                                else
                                {
                                    // mkvmerge
                                    mkvfile = mkvMerge(mp4file, audioTracks, subtileTracks);
                                    if (String.IsNullOrEmpty(mkvfile) == true)
                                    {
                                        db.ChangeEncodeJobStatus(Job, "Failed");
                                    }
                                    else if (Properties.Settings.Default.CheckFileSize == true && bitrate != Properties.Settings.Default.FallbackBitrate && Job.TargetFileSize >= Properties.Settings.Default.TargetSizeBitrateThreshold && new FileInfo(mkvfile).Length / (double)Job.TargetFileSize < 0.99)
                                    {
                                        AutoEncodeLog.Log2File(String.Format("mkvMerge has merged file \"{0}\", but file size is smaller than expected", mkvfile));
                                        db.ChangeEncodeJobStatus(Job, "Failed");
                                    }
                                    else
                                    {
                                        File.Delete(Job.OutputFilePath);
                                        File.Move(mkvfile, Job.OutputFilePath);
                                        AutoEncodeLog.Log2File(String.Format("File \"{0}\" has been successfully encoded", Job.OutputFilePath));
                                        db.ChangeEncodeJobStatus(Job, "Done");
                                    }
                                    File.Delete(mp4file);
                                }
                            }
                            File.Delete(avsfile);
                        }
                        if (d2vfile != "DirectShowSource") File.Delete(d2vfile);
                    }
                    foreach (var track in audioTracks)
                    {
                        File.Delete(track.audioPath);
                    }
                }
                foreach (var track in subtileTracks)
                {
                    File.Delete(track.idxPath);
                    File.Delete(track.subPath);
                }
            }
            catch (Exception ex)
            {
                AutoEncodeLog.Log2File(String.Format("Encountered an error during EncodeWorker(): {0}", ex.ToString()));
            }
            Interlocked.Decrement(ref CurrentEncodeJob[x264index]);
        }
        private string PostUrl(string url, string refer, string cookiestring, NameValueCollection nvc, NameValueCollection files)
        {
            string result;
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            CookieContainer cookie = new CookieContainer();
            string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
            foreach (var c in cookiestring.Split(new char[] {';'}, StringSplitOptions.RemoveEmptyEntries))
            {
                cookie.Add(new Cookie(c.Split('=')[0].Trim(), c.Split('=')[1].Trim(), "/", "chdbits.org"));
            }
            request.CookieContainer = cookie;
            request.Referer = refer;
            request.Method = "POST";
            request.AllowAutoRedirect = false;
            request.ContentType = "multipart/form-data; boundary=" + boundary;
            Stream rs = request.GetRequestStream();
            byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
            string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
            string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n";
            foreach (string key in nvc.Keys)
            {
                rs.Write(boundarybytes, 0, boundarybytes.Length);
                string formitem = string.Format(formdataTemplate, key, nvc[key]);
                byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
                rs.Write(formitembytes, 0, formitembytes.Length);
            }
            foreach (string file in files.Keys)
            {
                string filename = files[file];
                if (String.IsNullOrEmpty(filename) == false) filename = Path.GetFileName(filename);
                else filename = "";
                rs.Write(boundarybytes, 0, boundarybytes.Length);
                string header = string.Format(headerTemplate, file, filename);
                byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
                rs.Write(headerbytes, 0, headerbytes.Length);
                if (String.IsNullOrEmpty(filename) == false)
                {
                    byte[] filecontent = File.ReadAllBytes(files[file]);
                    rs.Write(filecontent, 0, filecontent.Length);
                }
            }
            byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
            rs.Write(trailer, 0, trailer.Length);
            rs.Close();

            try
            {
                using (HttpWebResponse wresp = (HttpWebResponse)request.GetResponse())
                {
                    if ((wresp.StatusCode != HttpStatusCode.Found) &&
                        (wresp.StatusCode != HttpStatusCode.Redirect) &&
                        (wresp.StatusCode != HttpStatusCode.Moved) &&
                        (wresp.StatusCode != HttpStatusCode.MovedPermanently))
                    {
                        AutoEncodeLog.Log2File(String.Format("PostUrl failed to get newly added torrent ID. Server response: {0}", new StreamReader(wresp.GetResponseStream()).ReadToEnd()));
                        result = "";
                    }
                    else
                    {
                        result = wresp.Headers["Location"];
                    }
                }
            }
            catch (Exception ex)
            {
                AutoEncodeLog.Log2File(String.Format("PostUrl failed to post url. Exception message: {0}", ex.ToString()));
                return "";
            }
            return result;
        }
        public bool DownloadFile(string url, string refer, string cookiestring, string outfile)
        {
            CookieAwareWebClient wc = new CookieAwareWebClient();
            wc.refer = refer;
            foreach (var c in cookiestring.Split(';'))
            {
                wc.cookie.Add(new Cookie(c.Split('=')[0].Trim(), c.Split('=')[1].Trim(), "/", "chdbits.org"));
            }
            try
            {
                wc.DownloadFile(url, outfile);
            }
            catch (Exception ex)
            {
                AutoEncodeLog.Log2File(String.Format("DownloadFile failed to download \"{0}\". Exception message: {1}", url, ex.ToString()));
                return false;
            }
            return true;
        }
        public void ReleaseWorker(ReleaseJobs Job)
        {
            ReleaseEnvironment Environment;
            string ReleaseDir = Path.Combine(Properties.Settings.Default.ReleaseJobPath, Job.ReleaseName);
            try
            {
                // move all encoded file to release dir
                foreach (var encode in Job.EncodeJobs)
                {
                    if (File.Exists(encode.OutputFilePath) == false) continue;
                    string outfilename = Path.Combine(ReleaseDir, Path.GetFileName(encode.OutputFilePath));
                    File.Delete(outfilename);
                    File.Move(encode.OutputFilePath, outfilename);
                }
                Environment = new ReleaseEnvironment(ReleaseDir, 8);

                Environment.Infos["RELEASECATALOG"] = Job.CatalogIndex.CatalogName;

                Environment.Infos["URL"] = Job.Url;

                Environment.Infos["SOURCETYPE"] = "HDTV MPEG-2 1080i";

                if (string.IsNullOrEmpty(Job.AudioLanguage) == true) Environment.Infos["ALANGUAGE"] = "Mandarin";
                else Environment.Infos["ALANGUAGE"] = Job.AudioLanguage;

                Environment.Infos["SUB"] = Job.Sub;

                Environment.Infos["AUTHOR"] = Job.Author;

                if (string.IsNullOrEmpty(Job.Language) == true) Environment.Infos["LANGUAGE"] = "Chinese";
                else Environment.Infos["LANGUAGE"] = Job.Language;

                Environment.Infos["DESCRIPTION"] = Job.Description;

                Environment.Infos["POSTER"] = Job.Poster;

                Environment.Infos["CASTIMG"] = Job.CastImg;

                if (string.IsNullOrEmpty(Job.NfoTemplate) == true) Environment.Infos["NFOFILE"] = "";
                else
                {
                    Environment.Infos["NFOFILE"] = Path.Combine(Properties.Settings.Default.TempPath, Environment.Infos["RELEASENAME"] + ".nfo");
                    File.WriteAllText(Environment.Infos["NFOFILE"], Environment.Parse(Job.NfoTemplate));
                }

                Environment.Infos["DESCFILE"] = Path.Combine(Properties.Settings.Default.TempPath, Environment.Infos["RELEASENAME"] + ".desc");
                File.WriteAllText(Environment.Infos["DESCFILE"], Environment.Parse(Job.DescriptionTemplate));

                Environment.MakeTorrent();

                if (string.IsNullOrEmpty(Environment.Infos["DESCFILE"]) == false && string.IsNullOrEmpty(Environment.Infos["TORRENTFILE"]) == false)
                {
                    string torrenturl, torrentid, torrentpath;
                    Match torrentmatch;
                    NameValueCollection param = new NameValueCollection();
                    NameValueCollection files = new NameValueCollection();
                    files.Add("file", Environment.Infos["TORRENTFILE"]);
                    param.Add("name", Environment.Infos["RELEASENAME"].Replace('.', ' '));
                    param.Add("small_descr", Job.SubTitle);
                    param.Add("url", Environment.Infos["URL"]);
                    files.Add("nfo", Environment.Infos["NFOFILE"]);
                    param.Add("color", "0");
                    param.Add("font", "0");
                    param.Add("size", "0");
                    param.Add("descr", File.ReadAllText(Environment.Infos["DESCFILE"]));
                    param.Add("type", Job.ReleaseCatalog.ToString());
                    param.Add("medium_sel", "7");       // Encode
                    param.Add("codec_sel", "1");        // H.264
                    param.Add("standard_sel", "3");     // 720p
                    param.Add("team_sel", "0");         // no pack
                    param.Add("audiocodec_sel", "5");   // CHDTV
                    torrenturl = PostUrl("http://chdbits.org/takeupload.php", "http://chdbits.org/upload.php", Properties.Settings.Default.Cookie, param, files);
                    torrentmatch = Regex.Match(torrenturl, @"details.php\?id=(\d+)&uploaded=1");
                    if (torrentmatch.Success == true)
                    {
                        torrentid = torrentmatch.Groups[1].Value;
                        torrentpath = Path.Combine(Properties.Settings.Default.TorrentPath, Environment.Infos["RELEASENAME"] + ".torrent");
                        if (DownloadFile("http://chdbits.org/download.php?id=" + torrentid, "http://chdbits.org/details.php?id=" + torrentid + "&hit=1", Properties.Settings.Default.Cookie, torrentpath) == true)
                        {
                            db.ChangeReleaseJobStatus(Job, "Done");
                        }
                        else
                        {
                            db.ChangeReleaseJobStatus(Job, "Failed");
                        }
                    }
                    else
                    {
                        AutoEncodeLog.Log2File(String.Format("Can't find torrent ID in server response \"{0}\"", torrenturl));
                        db.ChangeReleaseJobStatus(Job, "Failed");
                    }

                    File.Delete(Environment.Infos["DESCFILE"]);
                    File.Delete(Environment.Infos["TORRENTFILE"]);
                }
                else
                {
                    db.ChangeReleaseJobStatus(Job, "Failed");
                }
                if (string.IsNullOrEmpty(Environment.Infos["NFOFILE"]) == false) File.Delete(Environment.Infos["NFOFILE"]);
            }
            catch (Exception ex)
            {
                AutoEncodeLog.Log2File(String.Format("Encountered an error during ReleaseWorker(): {0}", ex.ToString()));
            }
            Interlocked.Decrement(ref CurrentReleaseJob);
        }
        public bool DoEncodeJob(int x264index)
        {
            EncodeJobs EncodeJob;
            lock (db)
            {
                var result = from encode in db.EncodeJobs
                             where encode.JobStatusIndex == db.JobStatusIndex.First(x => x.JobStatusName == "Wating")
                             orderby encode.ReleaseJobs.ReleaseCatalog ascending, encode.EncodeJobID ascending
                             select encode;
                if (result.Count() == 0) return false;
                EncodeJob = result.First();
                db.ChangeEncodeJobStatus(EncodeJob, "Processing");
            }
            Interlocked.Increment(ref CurrentEncodeJob[x264index]);
            Thread worker = new Thread(() => EncodeWorker(EncodeJob, x264index));
            worker.Start();
            return true;
        }
        public bool DoReleaseJob()
        {
            ReleaseJobs ReleaseJob;
            lock (db)
            {
                var result = from release in db.ReleaseJobs
                             where release.JobStatusIndex == db.JobStatusIndex.First(x => x.JobStatusName == "Wating") &&
                                    release.EncodeJobs.Count != 0 &&
                                    release.EncodeJobs.Count(x => x.JobStatusIndex == db.JobStatusIndex.First(y => y.JobStatusName == "Done")) == release.EncodeJobs.Count
                             select release;
                if (result.Count() == 0) return false;
                ReleaseJob = result.First();
                db.ChangeReleaseJobStatus(ReleaseJob, "Processing");
            }
            Interlocked.Increment(ref CurrentReleaseJob);
            LastReleaseTime = DateTime.Now;
            Thread worker = new Thread(() => ReleaseWorker(ReleaseJob));
            worker.Start();
            return true;
        }
        public void Run()
        {
            ServicePointManager.Expect100Continue = false;
            try
            {
                // create viewer thread
                Thread dbworker = new Thread(() => DBViewerWorker());
                dbworker.Start();
                for (; ; Thread.Sleep(60000))
                {
                    bool IsEncoded = false, IsReleased = false;
                    lock (db)
                    {
                        DoTemplateMatch();
                        int x264index = CheckEncodeOk();
                        if (x264index != -1)
                        {
                            if (DoEncodeJob(x264index) == true) IsEncoded = true;
                        }
                        if (CheckReleaseOk())
                        {
                            if (DoReleaseJob() == true) IsReleased = true;
                        }
                    }
                    if (IsEncoded == true) Thread.Sleep(900000);
                    else if (IsReleased == true) Thread.Sleep(600000);
                }
            }
            catch (Exception ex)
            {
                AutoEncodeLog.Log2File(String.Format("Encountered an error during Run(): {0}", ex.ToString()));
            }
        }
        public void DBViewerWorker()
        {
            for (; ; )
            {
                sem.WaitOne();
                lock (db)
                {
                    Application.Run(new Viewer(db));
                }
            }
        }
    }
}
